/*
与网页端进行通信的函数接口
*/

#include <stdio.h>
#include "http.h"

double rate = 0.0;
double O2 = 0.0;

// 获取请求
int get_line(int sock, char *buf)
{
    char ch = '\0'; // 一个字符一个字符处理请求
    int i = 0;
    ssize_t ret = 0;
    // 循环获取请求
    while (i < SIZE && ch != '\n')
    {
        // 读取请求的字符
        ret = recv(sock, &ch, 1, 0);
        // 获取请求报头
        if (ret > 0 && ch == '\r') // 读到回车符
        {
            // 再向后查看一个字符
            ssize_t s = recv(sock, &ch, 1, MSG_PEEK);
            if (s > 0 && ch == '\n') // 读到换行符
            {
                recv(sock, &ch, 1, 0); // 获取换行符
            }
            else
            {
                ch = '\n'; // 手动添加换行符
            }
        }
        buf[i++] = ch;
    }
    buf[i] = '\0'; // 封底
    return i;
}

// 错误处理
void echo_error(int sock, int err_code)
{
    switch (err_code)
    {
    case 403:
        show_403(sock);
        break;
    case 404:
        show_404(sock);
        break;
    case 405:
        show_405(sock);
        break;
    case 500:
        show_500(sock);
        break;
    defaut:
        break;
    }
}

// 清空消息报头
void clear_header(int sock)
{
    char buf[SIZE];
    int ret = 0;
    do
    {
        ret = get_line(sock, buf); // 循环获取请求报头
    } while (ret != 1 && (strcmp(buf, "\n") != 0)); // 获取到最后只有一个字符和换行符
}

// 403错误处理
void show_403(int sock)
{
    clear_header(sock);                            // 清空缓冲区
    char *msg = "HTTP/1.0 403 Not Permission\r\n"; // 响应状态行
    send(sock, msg, strlen(msg), 0);               // 发送状态行
    send(sock, "\r\n", strlen("\r\n"), 0);         // 发送空行
    struct stat st;                                // 文件属性
    stat("wwwroot/403.html", &st);                 // 获取网页文件属性
    int fd = open("wwwroot/403.html", O_RDONLY);   // 只读的方式打开文件
    sendfile(sock, fd, NULL, st.st_size);          // 发送文件内容
    close(fd);                                     // 关闭文件描述符
}

// 404错误处理
void show_404(int sock)
{
    clear_header(sock);                          // 清空缓冲区
    char *msg = "HTTP/1.0 404 Not Found\r\n";    // 响应状态行
    send(sock, msg, strlen(msg), 0);             // 发送状态行
    send(sock, "\r\n", strlen("\r\n"), 0);       // 发送空行
    struct stat st;                              // 文件属性
    stat("wwwroot/404.html", &st);               // 获取网页文件属性
    int fd = open("wwwroot/404.html", O_RDONLY); // 只读的方式打开文件
    sendfile(sock, fd, NULL, st.st_size);        // 发送文件内容
    close(fd);                                   // 关闭文件描述符
}

// 405错误处理
void show_405(int sock)
{
    clear_header(sock);                                // 清空缓冲区
    char *msg = "HTTP/1.0 405 Method Not Allowed\r\n"; // 响应状态行
    send(sock, msg, strlen(msg), 0);                   // 发送状态行
    send(sock, "\r\n", strlen("\r\n"), 0);             // 发送空行
    struct stat st;                                    // 文件属性
    stat("wwwroot/405.html", &st);                     // 获取网页文件属性
    int fd = open("wwwroot/405.html", O_RDONLY);       // 只读的方式打开文件
    sendfile(sock, fd, NULL, st.st_size);              // 发送文件内容
    close(fd);                                         // 关闭文件描述符
}

// 500错误处理
void show_500(int sock)
{
    clear_header(sock);                                   // 清空缓冲区
    char *msg = "HTTP/1.0 500 Internal Server Error\r\n"; // 响应状态行
    send(sock, msg, strlen(msg), 0);                      // 发送状态行
    send(sock, "\r\n", strlen("\r\n"), 0);                // 发送空行
    struct stat st;                                       // 文件属性
    stat("wwwroot/500.html", &st);                        // 获取网页文件属性
    int fd = open("wwwroot/500.html", O_RDONLY);          // 只读的方式打开文件
    sendfile(sock, fd, NULL, st.st_size);                 // 发送文件内容
    close(fd);                                            // 关闭文件描述符
}

// 返回ｇｅｔ请求需要的资源
int echo_www(int sock, const char *path, size_t s)
{
    int fd = open(path, O_RDONLY); // 打开所需的文件
    if (fd < 0)
    {
        echo_error(sock, 403); // 打开失败返回403
        return 7;              // 返回７,没有权限
    }

    char *msg = "HTTP/1.0 200 OK\r\n";     // 打开成功
    send(sock, msg, strlen(msg), 0);       // 发送状态行
    send(sock, "\r\n", strlen("\r\n"), 0); // 发送空行

    if (sendfile(sock, fd, NULL, s) < 0) // 发送文件内容
    {
        echo_error(sock, 500); // 发送失败返回服务器异常
        return 8;              // 返回８，服务器异常
    }
    close(fd); // 关闭文件描述符
    return 0;
}

// 手动处理ｐｏｓｔ请求和ｇｅｔ请求的参数
int handle_request(int sock, const char *method, const char *path, const char *query_string)
{
    char line[SIZE];      // 存放请求数据和ｇｅｔ请求的参数
    int ret = 0;          // 返回值
    int content_len = -1; // 数据的长度

    // get请求直接跳过，最后一起处理
    if (strcasecmp(method, "GET") == 0)
    {
        // 清空消息报头
        clear_header(sock);
    }
    else
    {
        // 获取post方法的参数大小
        do
        {
            ret = get_line(sock, line);                       // 获取数据内容的长度
            if (strncasecmp(line, "content-length", 14) == 0) // post的消息体记录正文长度的字段
            {
                content_len = atoi(line + 16); // 求出正文的长度
            }
        } while (ret != 1 && (strcmp(line, "\n") != 0)); // 读到最后只剩下换行符
    }

#ifdef DEBUG
    printf("method = %s\n", method);             // 请求方法
    printf("query_string = %s\n", query_string); // get请求的参数
    printf("content_len = %d\n", content_len);   // 正文长度
#endif

    char req_buf[SIZE] = {0};

    // 如果是POST方法，那么肯定携带请求数据，那么需要把数据解析出来
    if (strcasecmp(method, "POST") == 0)
    {
        int len = recv(sock, req_buf, content_len, 0);
#ifdef DEBUG
        printf("len = %d\n", len);         // 数据的长度
        printf("req_buf = %s\n", req_buf); // 数据内容json格式
#endif
    }

    // 处理发送的数据
    parse_and_process(sock, query_string, req_buf);
    return 0;
}

// 处理浏览器请求
int handle_requests(int sock)
{
    // 接收缓冲区
    char sockbuf[SIZE] = {0};

    // 从缓冲区读取数据
    recv(sock, sockbuf, SIZE, MSG_PEEK);

#ifdef DEBUG // 调试信息，检查网页端发来的数据
    printf("------------------------\n");
    printf("recv msg: %s\n", sockbuf);
    printf("------------------------\n");
#endif

    char buf[SIZE];                  // 接收整个请求
    int count = get_line(sock, buf); // 拿到请求的长度
    int ret = 0;                     // 读取请求时读取到的长度
    char method[32];
    char url[SIZE];
    char *query_string = NULL;
    int i = 0;
    int j = 0;
    int need_handle = 0; // 需要手动处理请求内容的标志位

    // 获取请求方法
    while (j < count)
    {
        if (isspace(buf[j])) // 读到空格就退出循环
        {
            break;
        }
        method[i] = buf[j]; // 　拿到请求方法
        i++;
        j++;
    }
    method[i] = '\0';

    while (isspace(buf[j]) && j < SIZE) // 过滤空格
    {
        j++;
    } // 拿到请求方法，指向请求路径(url)

    // 判断发过来的请求是GET还是POST了
    if (strcasecmp(method, "POST") && strcasecmp(method, "GET"))
    {
        printf("method failed\n"); // 如果都不是，那么提示一下
        // echo_error(sock, 405);     // 返回405资源
        ret = 5;  // 标定返回５
        goto end; // 跳转到ｅｎｄ结束函数
    }

    if (strcasecmp(method, "POST") == 0) // 如果是ｐｏｓｔ请求就将手动处理标志位置１
    {
        need_handle = 1;
    }

    i = 0;            // 循环计数清零
    while (j < count) // 循环获取请求路径（ｕｒｌ）
    {
        if (isspace(buf[j])) // 读到空格就结束循环
        {
            break;
        }
        if (buf[j] == '?') // ？后面是请求路径的参数
        {
            query_string = &url[i]; // query_string指向附带数据
            query_string++;
            url[i] = '\0'; // 将资源路径（和附带数据，如果有的话）保存再url中
        }
        else // 没有读到？的话就继续获取请求路径
        {
            url[i] = buf[j];
        }
        j++;
        i++;
    }
    url[i] = '\0'; // 封底

#ifdef DEBUG

    printf("query_string = %s\n", query_string); // 打印ｕｒｌ路径
    printf("url = %s\n", url);

#endif

    // 如果是ＧＥＴ请求并且有参数就手动处理，手动处理标志位置位
    if (strcasecmp(method, "GET") == 0 && query_string != NULL)
    {
        need_handle = 1;
    }

    char path[SIZE];               // 获取资源路径
    sprintf(path, "./web%s", url); // 资源路径标定为ｗｗｗｒｏｏｔ

#ifdef DEBUG

    printf("path = %s\n", path); // 打印资源地址
#endif

    // 如果请求的资源不存在，返回４０４
    struct stat st;          // 获取文件属性
    if (stat(path, &st) < 0) // 获取客户端请求的资源文件属性
    {
        printf("can't find file\n"); // 服务器打印信息
        echo_error(sock, 404);       // 返回４０４界面
        ret = 6;                     // 返回值标定为６
        goto end;                    // 跳转到ｅｎｄ结束函数
    }

    // 需要手动处理ｐｏｓｔ请求和ｇｅｔ请求带参数
    if (need_handle)
    {
        // 调用手动处理函数
        ret = handle_request(sock, method, path, query_string);
    }
    else
    {
        clear_header(sock);
        // 如果是GET方法，而且没有参数，则直接返回资源
        ret = echo_www(sock, path, st.st_size);
    }

end:
    close(sock); // 关闭套接字
    return ret;  // 返回值，程序运行结果
}

// 解析处理
int parse_and_process(int sock, const char *query_string, const char *input)
{
    printf("接收到数据请求数据，开始解析\n");
    cJSON *root = cJSON_Parse(input);
    while (root == NULL)
    {
        printf("解析失败,重新解析\n");
        root = cJSON_Parse(input);
        return -1;
    }
    printf("请求转化成json\n");
    if (cJSON_HasObjectItem(root, "msg"))
    {
        cJSON *item = cJSON_GetObjectItem(root, "msg");
        printf("获取msg\n");
        if (strcmp(item->valuestring, "get") == 0)
        {
            printf("获取数据\n");
            char *msg = get_msg(root);
            reply(sock, msg);
        }
        else if (strcmp(item->valuestring, "set") == 0)
        {
            printf("设置数据\n");
            char *msg = set_msg(root);
            reply(sock, msg);
        }
    }
    else if (cJSON_HasObjectItem(root, "login"))
    {
        cJSON *login = cJSON_GetObjectItem(root, "login");
        printf("获取login\n");
        char *msg = login_msg(login);
        reply(sock, msg);
    }
    else if (cJSON_HasObjectItem(root, "patient"))
    {
        cJSON *patient = cJSON_GetObjectItem(root, "patient");
        printf("获取patient\n");
        char *msg = patient_msg(patient);
        reply(sock, msg);
    }
    else if (cJSON_HasObjectItem(root, "register"))
    {
        cJSON *register1 = cJSON_GetObjectItem(root, "register");
        printf("获取register\n");
        char *msg = register_msg(register1);
        reply(sock, msg);
    }
}

// 回复响应
void reply(int sock, char *msg)
{
    char buf[SIZE];
    int len = strlen(msg);
    sprintf(buf, "HTTP/1.1 200 OK\r\nContent-Type: text/html\r\n\
    Content-Length: %d\r\n\r\n%s",
            len, msg);
    send(sock, buf, strlen(buf), 0);
}

// 　通过共享内存获取数据，并发送
char *get_msg(cJSON *root)
{
    // 创建共享内存并建立映射关系
    int shmid;
    std_node_t *shmaddr = creat_shm(&shmid);
    getmsg__json_t msg;
    // 从共享内存中获取数据
    for (int i = 0; i < 25; i++)
    {
        switch (shmaddr[i].key)
        {
        case 202:
            msg.temperature = shmaddr[i].new_val.f_val;
            break;
        case 203:
            msg.humidity = shmaddr[i].new_val.f_val;
            break;
        case 106:
            msg.heart_rate = shmaddr[i].new_val.f_val;
            break;
        case 107:
            msg.O2 = shmaddr[i].new_val.f_val;
            break;
        default:
            break;
        }
    }

    // 将数据存放到ｊｓｏｎ结构中
    root = cJSON_SetObjectValue(root, "temperature", msg.temperature);
    root = cJSON_SetObjectValue(root, "humidity", msg.humidity);
    root = cJSON_SetObjectValue(root, "heart_rate", msg.heart_rate);
    root = cJSON_SetObjectValue(root, "O2", msg.O2);

    // 反序列化
    char *input = cJSON_Print(root);

    return input;
}

// 通过消息队列发送网页过来的指令
char *set_msg(cJSON *root)
{
    // 创建共享内存
    int shmid;
    std_node_t *shmaddr = creat_shm(&shmid);

    // 解析json结构
    cJSON *flame = cJSON_GetObjectItem(root, "flame");
    cJSON *infrared = cJSON_GetObjectItem(root, "infrared");
    cJSON *ozone = cJSON_GetObjectItem(root, "ozone");
    cJSON *ultraviolet = cJSON_GetObjectItem(root, "ultraviolet");
    cJSON *fan = cJSON_GetObjectItem(root, "fan");
    cJSON *call = cJSON_GetObjectItem(root, "call");
    cJSON *sprayer = cJSON_GetObjectItem(root, "sprayer");

    // 将数据放入共享内存
    printf("数据即将提交到共享内存\n");
    for (int i = 0; i < 25; i++)
    {
        switch (shmaddr[i].key)
        {
        case 204:
            shmaddr[i].new_val.i_val = infrared->valueint;
            break;
        case 205:
            shmaddr[i].new_val.i_val = flame->valueint;
            break;
        case 206:
            shmaddr[i].new_val.i_val = call->valueint;
            break;
        case 208:
            shmaddr[i].new_val.i_val = ozone->valueint;
            break;
        case 209:
            shmaddr[i].new_val.i_val = ultraviolet->valueint;
            break;
        case 210:
            shmaddr[i].new_val.i_val = fan->valueint;
            break;
        case 211:
            shmaddr[i].new_val.i_val = sprayer->valueint;
            break;
        default:
            break;
        }
    }
    printf("数据提交到共享内存\n");
    return "set ok";
}

// 登录
char *login_msg(cJSON *root)
{
    char p[1024] = {0};
    strcpy(p, cJSON_Print(root));
    printf("root = %s\n", p);
    char *sql;
    char *buf = (char *)malloc(4096);
    // 打开数据库
    sqlite3 *db = creat_SQL("../user.db");
    // 创建用户表
    creat_userTable(db);
    printf("创建用户表\n");
    user_t user;
    printf("定义user结构体\n");
    // 从json中获取用户名和密码
    for (int i = 0; i < 3; i++)
    {
        printf("循环获取数据%d\n", i);
        cJSON *item = cJSON_GetArrayItem(root, i);
        switch (cJSON_GetObjectItem(item, "key")->valueint)
        {
        case 301:
            strcpy(user.username, cJSON_GetObjectItem(item, "value")->valuestring);
            break;
        case 302:
            strcpy(user.password, cJSON_GetObjectItem(item, "value")->valuestring);
            break;
        case 303:
            user.authority = cJSON_GetObjectItem(item, "value")->valueint;
            break;
        default:
            break;
        }
    }

    // 查找用户是否存在
    sprintf(sql, "select * from user where username = '%s' and password = '%s'", user.username, user.password);
    int ret = select_value(db, "user", sql, &buf);
    printf("遍历数据库\n");
    free(buf);
    if (ret <= 0)
    {
        return "login err";
    }
    else
    {
        return "login ok";
    }
}

// 注册
char *register_msg(cJSON *root)
{
    user_t user;
    // 创建共享内存
    int shmid;
    std_node_t *shmaddr = creat_shm(&shmid);
    // 从json中获取用户名和密码
    for (int i = 0; i < 3; i++)
    {
        cJSON *item = cJSON_GetArrayItem(root, i);
        switch (cJSON_GetObjectItem(item, "key")->valueint)
        {
        case 301:
            strcpy(user.username, cJSON_GetObjectItem(item, "value")->valuestring);
            break;
        case 302:
            strcpy(user.password, cJSON_GetObjectItem(item, "value")->valuestring);
            break;
        case 303:
            user.authority = cJSON_GetObjectItem(item, "value")->valueint;
            break;
        default:
            break;
        }
    }
    // 将数据放到共享内存里
    for (int i = 22; i < 25; i++)
    {
        switch (shmaddr[i].key)
        {
        case 301:
            strcpy(shmaddr[i].new_val.str, user.username);
            printf("username = %s\n", shmaddr[i].new_val.str);
            break;
        case 302:
            strcpy(shmaddr[i].new_val.str, user.password);
            printf("password = %s\n", shmaddr[i].new_val.str);
            break;
        case 303:
            shmaddr[i].new_val.i_val = user.authority;
            printf("authority = %d\n", shmaddr[i].new_val.i_val);
            break;
        default:
            break;
        }
    }
    return "register ok";
}

// 病人信息
char *patient_msg(cJSON *root)
{
    // 创建共享内存
    int shmid;
    std_node_t *shmaddr = creat_shm(&shmid);

    // 从json中获取病人信息
    patient_json_t patient;
    for (int i = 0; i < 11; i++)
    {
        printf("循环获取病人信息%d\n", i);
        if (i == 0)
        {
            char buf[SIZE] = {0};
            strcpy(buf, cJSON_Print(root));
            printf("发送的json数组为\n%s\n", buf);
        }
        cJSON *item = cJSON_GetArrayItem(root, i);
        if (item == NULL)
        {
            printf("json数据转化失败\n");
            break;
        }
        switch (cJSON_GetObjectItem(item, "key")->valueint)
        {
        case 101:
            patient.patientID = cJSON_GetObjectItem(item, "value")->valueint;
            break;
        case 102:
            strcpy(patient.patientName, cJSON_GetObjectItem(item, "value")->valuestring);
            break;
        case 103:
            patient.patientAge = cJSON_GetObjectItem(item, "value")->valueint;
            break;
        case 104:
            patient.patientGender = cJSON_GetObjectItem(item, "value")->valueint;
            break;
        case 105:
            strcpy(patient.illness, cJSON_GetObjectItem(item, "value")->valuestring);
            break;
        case 106:
            patient.heart_rate = cJSON_GetObjectItem(item, "value")->valuedouble;
            break;
        case 107:
            patient.O2 = cJSON_GetObjectItem(item, "value")->valuedouble;
            break;
        case 108:
            patient.roomID = cJSON_GetObjectItem(item, "value")->valueint;
            break;
        case 109:
            patient.doctorID = cJSON_GetObjectItem(item, "value")->valueint;
            break;
        case 110:
            strcpy(patient.doctorName, cJSON_GetObjectItem(item, "value")->valuestring);
            break;
        case 111:
            strcpy(patient.time, cJSON_GetObjectItem(item, "value")->valuestring);
            break;
        default:
            break;
        }
    }
    printf("已获取病人信息\n");
    // 将病人信息放到共享内存中
    for (int i = 0; i < 25; i++)
    {
        switch (shmaddr[i].key)
        {
        case 101:
            shmaddr[i].new_val.i_val = patient.patientID;
            break;
        case 102:
            strcpy(shmaddr[i].new_val.str, patient.patientName);
            break;
        case 103:
            shmaddr[i].new_val.i_val = patient.patientAge;
            break;
        case 104:
            shmaddr[i].new_val.i_val = patient.patientGender;
            break;
        case 105:
            strcpy(shmaddr[i].new_val.str, patient.illness);
            break;
        case 106:
            shmaddr[i].new_val.f_val = patient.heart_rate;
            printf("heart_rate = %.2f\n", shmaddr[i].new_val.f_val);
            break;
        case 107:
            shmaddr[i].new_val.f_val = patient.O2;
            printf("O2 = %.2f\n", shmaddr[i].new_val.f_val);
            break;
        case 108:
            shmaddr[i].new_val.i_val = patient.roomID;
            break;
        case 109:
            shmaddr[i].new_val.i_val = patient.doctorID;
            break;
        case 110:
            strcpy(shmaddr[i].new_val.str, patient.doctorName);
            break;
        case 111:
            strcpy(shmaddr[i].new_val.str, patient.time);
            break;
        default:
            break;
        }
    }
    return "patient ok";
}
